其他
OpenHarmony源码系列: 鸿蒙页面背后的机制,打通 JS View 与C++世界
本文作者
作者:Pika
链接:
https://juejin.cn/post/7347221041569218611
本文由作者授权发布。
注意鸿蒙系统一直在更新,源码可能会有变更,但是核心体系结构变化不会太大,依旧可以帮助大家建立对鸿蒙源码体系的认知。
引言
本篇是ArkUI Engine系列的第二篇,通过学习ViewPU与Component的关系,我们能够知道在ArkUI中写的一系列Component的具体实现,打通JS View与native C++的世界。
@Component
struct HelloArkUI{
build(){
Row(){
Text("文本1")
Text("文本2")
}
}
}
class HelloArkUI extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
this.setInitiallyProvidedValue(params);
}
setInitiallyProvidedValue(params) {
}
updateStateVars(params) {
}
purgeVariableDependenciesOnElmtId(rmElmtId) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id__());
this.aboutToBeDeletedInternal();
}
initialRender() {
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Row.create();
if (!isInitialRender) {
Row.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create("文本1");
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
创建完成Text后立即调用pop函数
Text.pop();
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create("文本2");
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
当前Row的子组件完成之后,才会调用自身的pop函数
Row.pop();
}
rerender() {
this.updateDirtyElements();
}
}
protected abstract initialRender(): void;
protected abstract rerender(): void;
public observeComponentCreation(compilerAssignedUpdateFunc: UpdateFunc): void {
if (this.isDeleting_) {
stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation `);
return;
}
内部定义了一个更新方法
const updateFunc = (elmtId: number, isFirstRender: boolean) => {
stateMgmtConsole.debug(`${this.debugInfo__()}: ${isFirstRender ? `First render` : `Re-render/update`} start ....`);
this.currentlyRenderedElmtIdStack_.push(elmtId);
compilerAssignedUpdateFunc(elmtId, isFirstRender);
this.currentlyRenderedElmtIdStack_.pop();
stateMgmtConsole.debug(`${this.debugInfo__()}: ${isFirstRender ? `First render` : `Re-render/update`} - DONE ....`);
}
通过ViewStackProcessor的AllocateNewElmetIdForNextComponent为当前创建的组件赋予一个全局id
const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent();
// in observeComponentCreation function we do not get info about the component name, in
// observeComponentCreation2 we do.
this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc });
// add element id -> owning ViewPU
UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this));
try {
调用更新方法,即上面的updateFunc,第一次创建为true
updateFunc(elmtId, /* is first render */ true);
} catch (error) {
// avoid the incompatible change that move set function before updateFunc.
this.updateFuncByElmtId.delete(elmtId);
UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId);
stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${(error as Error).message}`);
throw error;
}
}
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Row.create();
初始化不执行这里
if (!isInitialRender) {
Row.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
下面我们来看create函数与pop函数究竟做了什么,它的实现在哪。
void JSRow::JSBind(BindingTarget globalObj)
{
JSClass<JSRow>::Declare("Row");
MethodOptions opt = MethodOptions::NONE;
JSClass<JSRow>::StaticMethod("create", &JSRow::Create, opt);
JSClass<JSRow>::StaticMethod("createWithWrap", &JSRow::CreateWithWrap, opt);
JSClass<JSRow>::StaticMethod("fillParent", &JSFlex::SetFillParent, opt);
JSClass<JSRow>::StaticMethod("wrapContent", &JSFlex::SetWrapContent, opt);
JSClass<JSRow>::StaticMethod("justifyContent", &JSRow::SetJustifyContent, opt);
JSClass<JSRow>::StaticMethod("alignItems", &JSRow::SetAlignItems, opt);
JSClass<JSRow>::StaticMethod("alignContent", &JSFlex::SetAlignContent, opt);
JSClass<JSRow>::StaticMethod("height", &JSFlex::JsHeight, opt);
JSClass<JSRow>::StaticMethod("width", &JSFlex::JsWidth, opt);
JSClass<JSRow>::StaticMethod("size", &JSFlex::JsSize, opt);
JSClass<JSRow>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
JSClass<JSRow>::StaticMethod("onHover", &JSInteractableView::JsOnHover);
JSClass<JSRow>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
JSClass<JSRow>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
JSClass<JSRow>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
JSClass<JSRow>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
JSClass<JSRow>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
JSClass<JSRow>::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage);
JSClass<JSRow>::StaticMethod("pointLight", &JSViewAbstract::JsPointLight, opt);
JSClass<JSRow>::InheritAndBind<JSContainerBase>(globalObj);
void JSRow::Create(const JSCallbackInfo& info)
{
std::optional<CalcDimension> space;
if (info.Length() > 0 && info[0]->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
JSRef<JSVal> spaceVal = obj->GetProperty("space");
CalcDimension value;
if (ParseJsDimensionVp(spaceVal, value)) {
space = value;
} else if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TEN)) {
space = Dimension();
}
}
VerticalAlignDeclaration* declaration = nullptr;
if (info.Length() > 0 && info[0]->IsObject()) {
JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
JSRef<JSVal> useAlign = obj->GetProperty("useAlign");
if (useAlign->IsObject()) {
declaration = JSRef<JSObject>::Cast(useAlign)->Unwrap<VerticalAlignDeclaration>();
}
}
RowModel::GetInstance()->Create(space, declaration, "");
}
Component建立
void RowModelImpl::Create(const std::optional<Dimension>& space, AlignDeclaration* declaration, const std::string& tag)
{
std::list<RefPtr<Component>> children;
RefPtr<RowComponent> rowComponent =
AceType::MakeRefPtr<OHOS::Ace::RowComponent>(FlexAlign::FLEX_START, FlexAlign::CENTER, children);
ViewStackProcessor::GetInstance()->ClaimElementId(rowComponent);
rowComponent->SetMainAxisSize(MainAxisSize::MIN);
rowComponent->SetCrossAxisSize(CrossAxisSize::MIN);
if (space.has_value() && space->Value() >= 0.0) {
rowComponent->SetSpace(space.value());
}
if (declaration != nullptr) {
rowComponent->SetAlignDeclarationPtr(declaration);
}
ViewStackProcessor::GetInstance()->Push(rowComponent);
}
void ViewStackProcessor::Push(const RefPtr<Component>& component, bool isCustomView)
{
CHECK_NULL_VOID(component);
std::unordered_map<std::string, RefPtr<Component>> wrappingComponentsMap;
if (componentsStack_.size() > 1 && ShouldPopImmediately()) {
Pop();
}
之后这里存入了一个键值对,main 对应着我们当前创建的component,本例子就是RowComponent
wrappingComponentsMap.emplace("main", component);
componentsStack_.push(wrappingComponentsMap
bool ViewStackProcessor::ShouldPopImmediately()
{
auto type = AceType::TypeName(GetMainComponent());
auto componentGroup = AceType::DynamicCast<ComponentGroup>(GetMainComponent());
auto multiComposedComponent = AceType::DynamicCast<MultiComposedComponent>(GetMainComponent());
auto soleChildComponent = AceType::DynamicCast<SoleChildComponent>(GetMainComponent());
auto menuComponent = AceType::DynamicCast<MenuComponent>(GetMainComponent());
return (strcmp(type, AceType::TypeName<TextSpanComponent>()) == 0 ||
!(componentGroup || multiComposedComponent || soleChildComponent || menuComponent));
}
void ViewStackProcessor::Pop()
{
if (componentsStack_.empty() || componentsStack_.size() == 1) {
return;
}
auto component = WrapComponents().first;
if (AceType::DynamicCast<ComposedComponent>(component)) {
auto childComponent = AceType::DynamicCast<ComposedComponent>(component)->GetChild();
SetZIndex(childComponent);
SetIsPercentSize(childComponent);
} else {
SetZIndex(component);
SetIsPercentSize(component);
}
更新每一个RenderComponent的位置
UpdateTopComponentProps(component);
componentsStack_.pop();
判断key为main的Componet是否需要添加把子组件加入自身。本例子是当Text创建时便会被加入Row的子组件
auto componentGroup = AceType::DynamicCast<ComponentGroup>(GetMainComponent());
auto multiComposedComponent = AceType::DynamicCast<MultiComposedComponent>(GetMainComponent());
if (componentGroup) {
componentGroup->AppendChild(component);
} else if (multiComposedComponent) {
multiComposedComponent->AddChild(component);
} else {
auto singleChild = AceType::DynamicCast<SingleChild>(GetMainComponent());
if (singleChild) {
singleChild->SetChild(component);
}
}
}
确定z轴关系:子组件在父组件之上,同时更新顺序。 确定父子关系:根据Component的不同调用不同的方法加入子组件。
this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Text.create("文本2");
if (!isInitialRender) {
Text.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});
Text.pop();
Row.pop();
class JSContainerBase : public JSViewAbstract, public JSInteractableView {
public:
static void Pop();
static void JSBind(BindingTarget globalObj);
};
void JSContainerBase::Pop()
{
ViewStackModel::GetInstance()->PopContainer();
}
void ViewStackProcessor::PopContainer()
{
auto type = AceType::TypeName(GetMainComponent());
auto componentGroup = AceType::DynamicCast<ComponentGroup>(GetMainComponent());
auto multiComposedComponent = AceType::DynamicCast<MultiComposedComponent>(GetMainComponent());
auto soleChildComponent = AceType::DynamicCast<SoleChildComponent>(GetMainComponent());
if ((componentGroup && strcmp(type, AceType::TypeName<TextSpanComponent>()) != 0) || multiComposedComponent ||
soleChildComponent) {
Pop();
return;
}
while ((!componentGroup && !multiComposedComponent && !soleChildComponent) ||
strcmp(type, AceType::TypeName<TextSpanComponent>()) == 0) {
if (componentsStack_.size() <= 1) {
break;
}
Pop();
type = AceType::TypeName(GetMainComponent());
componentGroup = AceType::DynamicCast<ComponentGroup>(GetMainComponent());
multiComposedComponent = AceType::DynamicCast<MultiComposedComponent>(GetMainComponent());
soleChildComponent = AceType::DynamicCast<SoleChildComponent>(GetMainComponent());
}
Pop();
}
Element创建
class ACE_EXPORT Component : public virtual AceType {
DECLARE_ACE_TYPE(Component, AceType);
public:
Component();
~Component() override;
virtual RefPtr<Element> CreateElement() = 0;
RefPtr<Element> Element::InflateComponent(const RefPtr<Component>& newComponent, int32_t slot, int32_t renderSlot)
{
// confirm whether there is a reuseable element.
auto retakeElement = RetakeDeactivateElement(newComponent);
if (retakeElement) {
retakeElement->SetNewComponent(newComponent);
retakeElement->Mount(AceType::Claim(this), slot, renderSlot);
if (auto node = retakeElement->GetRenderNode()) {
node->SyncRSNode(node->GetRSNode());
}
return retakeElement;
}
RefPtr<Element> newChild = newComponent->CreateElement();
if (newChild) {
newChild->SetNewComponent(newComponent);
newChild->Mount(AceType::Claim(this), slot, renderSlot);
}
return newChild;
}
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!